<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 2015/3/4
 * Time: 19:44
 * Use :
 * File: AbstractQuery.php
 */

namespace ulue\core\db\base\query;

use Trigger;
use ulue\core\db\base\AbstractDbDriver;
use ulue\core\db\DriverFactory;
use ulue\core\abstracts\AbstractCore;

abstract class AbstractQuery extends AbstractCore implements InterfaceQuery,\ArrayAccess,\IteratorAggregate
{

    /**
     * @var AbstractDbDriver
     */
    protected $db   = null;


    /**
     * @var string
     */
    protected $name;

    /**
     * 设置查询多条记录[all()]时, 填充数据模型时 使用表的某一列作为索引键
     * e.g. id
     * @var string|null
     */
    protected $key=null;

    /**
     * @var array
     */
    // protected $elements     = [];

    protected $queryString;

    /**
     * 正在操作的表名
     * @var null
     */
    protected $table = null;

    /**
     * Property nameQuote.
     * @var string
     */
    protected $nameQuote    = '`';

    /**
     * @param object|AbstractDbDriver $dbDriver object
     * @internal param array $elements
     */
    public function __construct(AbstractDbDriver $dbDriver=null)
    {
        $this->db = is_object($dbDriver) ? $dbDriver : DriverFactory::getLastDriver($this->name);
    }


############################## find sql element function ##################################

    public function setTable($name)
    {
        $this->table = $name;

        return $this;
    }

    public function getTable()
    {
        return $this->table;
    }

    /**
     * @param string $sql
     * @return mixed
     */
    public function explain($sql='')
    {
        if (!$sql) {
            $db = $this->db->setSql( 'EXPLAIN '.$this->toString() );
        } else {
            $db = $this->db->setSql( 'EXPLAIN '.$sql );
        }

        return $db->queryAll();
    }

    /**
     * @param array $data
     * @param null $table
     * @return mixed
     */
    abstract public function buildCreate(array $data, $table=null);

    /**
     * @param string $value
     * @return string
     */
    abstract public function toString(); // buildQuery

    /**
     * @param array $data
     * @param null $table
     * @return string
     */
    abstract public function buildUpdate(array $data, $table=null);

    /**
     * @return mixed
     */
    abstract public function buildDelete();

    /**
     * Method to get property name
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Usage:
     * $query->quote('fulltext');
     * $query->q('fulltext');
     * $query->q(array('option', 'fulltext'));
     *
     * @param   mixed    $text    A string or an array of strings to quote.
     * @param   boolean  $escape  True to escape the string, false to leave it unchanged.
     * @return  string  The quoted input string.
     * @throws  \RuntimeException if the internal db property is not a valid object.
     */
    public function quote($text, $escape = true)
    {
        if (is_numeric($text) || is_null($text)) {
            return $text;
        }

        if (is_array($text) || is_object($text)) {
            $text = (array) $text;

            foreach ($text as $k => $v)
            {
                $text[$k] = $this->quote($v, $escape);
            }

            return $text;
        }

        return '\'' . ($escape ? $this->escape($text) : $text) . '\'';
    }

    /**
     * Proxy of quote().
     * @param   mixed    $text    A string or an array of strings to quote.
     * @param   boolean  $escape  True to escape the string, false to leave it unchanged.
     * @return  string
     */
    public function q($text, $escape = true)
    {
        return $this->quote($text, $escape);
    }

    /**
     * Usage:
     * $query->quoteName('#__a');
     * $query->qn('#__a');
     *
     * @param   mixed  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
     *                        Each type supports dot-notation name.
     * @return  mixed  The quote wrapped name, same type of $name.
     * @throws  \RuntimeException if the internal db property is not a valid object.
     */
    public function quoteName($name)
    {
        if (is_string($name)) {
            $pos = stripos($name, ' AS ');
            $quotedAlias = '';

            if ($pos !== false)
            {
                $alias = substr($name, $pos + 4);
                $name  = substr($name, 0, $pos);

                $quotedAlias = $this->quoteNameStr(array($alias));
            }

            $quotedName = $this->quoteNameStr(explode('.', $name));

            return $quotedName . ($quotedAlias ? ' AS ' . $quotedAlias : '');
        }
        elseif (is_array($name) || is_object($name)) {
            $fin = array();

            foreach ((array) $name as $n)
            {
                $fin[] = $this->quoteName($n);
            }

            return $fin;
        }

        return $name;
    }

    /**
     * Proxy of quoteName().
     * @param   mixed  $name  The identifier name to wrap in quotes, or an array of identifier names to wrap in quotes.
     *                        Each type supports dot-notation name.
     * @return  mixed  The quote wrapped name, same type of $name.
     */
    public function qn($name)
    {
        return $this->quoteName($name);
    }

    /**
     * Quote strings coming from quoteName call.
     * @param   array  $strArr  Array of strings coming from quoteName dot-explosion.
     * @return  string  Dot-imploded string of quoted parts.
     */
    protected function quoteNameStr($strArr)
    {
        $parts = array();
        $q = $this->nameQuote;

        foreach ($strArr as $part) {
            if (is_null($part))
            {
                continue;
            }

            if (strlen($q) == 1)
            {
                $parts[] = $q . $part . $q;
            } else
            {
                $parts[] = $q{0} . $part . $q{1};
            }
        }

        return implode('.', $parts);
    }

    public function escape($content)
    {
        return $content;
    }

    abstract public function getElements();

    static public function enabledElements()
    {
        return [];
    }

    public function setDb(AbstractDbDriver $value)
    {
        $this->db = $value;

        return $this;
    }

    public function getDb()
    {
        return $this->db;
    }

     /**
     * Defined by IteratorAggregate interface
     * Returns an iterator for this object, for use with foreach
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator( $this->getElements() );
    }


    /**
     * Checks whether an offset exists in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  boolean  True if the offset exists, false otherwise.
     */
    public function offsetExists($offset)
    {
        $elements = array_keys(static::enabledElements());

        return in_array($offset, $elements);
    }

    /**
     * Gets an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  mixed  The array value if it exists, null otherwise.
     */
    public function offsetGet($offset)
    {
        return $this->get($offset);
    }

    /**
     * Sets an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @param   mixed  $value   The array value.
     * @return  void
     */
    public function offsetSet($offset, $value)
    {
        $this->set($offset,$value);
    }

    /**
     * Unset an offset in the iterator.
     * @param   mixed  $offset  The array offset.
     * @return  void
     */
    public function offsetUnset($offset)
    {
        $this->set($offset,'');
    }

}// end class AbstractQuery